package com.ruoyi.workflow.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.common.core.domain.entity.SysDictData;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.file.FileUtils;
import com.ruoyi.system.service.ISysDictTypeService;
import com.ruoyi.workflow.domain.BpmnInfo;
import com.ruoyi.workflow.mapper.BpmnInfoMapper;
import com.ruoyi.workflow.service.IBpmnInfoService;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.image.ProcessDiagramGenerator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.InputStream;
import java.util.Arrays;
import java.util.List;
import java.util.zip.ZipInputStream;

import javax.annotation.Resource;
import javax.sql.rowset.serial.SerialException;

/**
 * 流程定义信息Service业务层处理
 *
 * @author xiaohao
 * @date 2022-11-14
 */
@Service
public class BpmnInfoServiceImpl extends ServiceImpl<BpmnInfoMapper, BpmnInfo> implements IBpmnInfoService {
    @Resource
    private BpmnInfoMapper bpmnInfoMapper;

    //    数据字典
    @Resource
    private ISysDictTypeService dictTypeService;
    //    流程定义部署
    @Resource
    private RepositoryService repositoryService;

//    图片生成
    @Autowired
    private ProcessDiagramGenerator processDiagramGenerator;

    /**
     * 查询流程定义信息列表
     *
     * @param bpmnInfo 流程定义信息
     * @return 流程定义信息
     */
    @Override
    public List<BpmnInfo> selectBpmnInfoList(BpmnInfo bpmnInfo) {
        return bpmnInfoMapper.selectBpmnInfoList(bpmnInfo);
    }


    @Transactional(rollbackFor = Exception.class)
    @Override
    public void deploy(BpmnInfo bpmnInfo, InputStream bpmnFile) {
//        1.获取流程定义的类型标签与字典类型
        List<SysDictData> dictData = dictTypeService.selectDictDataByType("sys_audit_type");
        for (SysDictData data : dictData) {
            //判断当前字典的值是否为当前流程类型的值
            if (data.getDictValue().equals(bpmnInfo.getBpmnType())) {
                bpmnInfo.setBpmnLabel(data.getDictLabel());
                break;
            }
        }

//        2.调用 activiti  进行流程部署
        Deployment deployment = null;
        String suffix = FileUtils.getSuffix(bpmnInfo.getResourceName());
        if ("zip".equals(suffix)) {
            deployment = repositoryService.createDeployment()
                    .addZipInputStream(new ZipInputStream(bpmnFile))//上传指定的Zip文件
                    .name(bpmnInfo.getBpmnLabel())//
                    .deploy();
        } else {
            deployment = repositoryService.createDeployment()//创建流程部署
//                classpath下的资源目录
                    .addInputStream(bpmnInfo.getBpmnType() + "bpmn20.xml", bpmnFile)
                    .name(bpmnInfo.getBpmnLabel())//定义流程名称
                    .deploy();//开始部署
        }
//    TODO    将其设置为 与指定字典类型相同的值 在获取
        String definitionKey = bpmnInfo.getBpmnType();//getBpmnType字典类型的值

//        3.保存部署后的相关属性
        bpmnInfo.setDeploymentId(deployment.getId());//部署ID
        bpmnInfo.setDeployTime(deployment.getDeploymentTime());//时间
//        将key 设置为与字典类型的value 相同 就可以不要写死了
        bpmnInfo.setProcessDefinitionKey(definitionKey);//流程定义 Key

//        4.基于部署后的 Key 查询；流程定义对象
        ProcessDefinition definition = repositoryService.createProcessDefinitionQuery()
                .processDefinitionKey(definitionKey)//字典管理的类型 == key
                .deploymentId(deployment.getId())//部署的id
                .singleResult();//查询返回当个对象
//        判断流程定义对象是否存在
        if (definition == null) {
            throw new ServiceException("流程定义的 key 不正确", 501);
        }

//           获取到版本号
        bpmnInfo.setVersion(Long.parseLong(definition.getVersion() + ""));//转换为Long类型
//        5.保存 bpmnInfo 对象
        super.save(bpmnInfo);


    }


    /*   部署流程定义删除*/
    @Transactional(rollbackFor = Exception.class)//多条语句操作 需要添加事物
    @Override
    public boolean deleteDefinition(Long[] ids) {
        for (Long id : ids) {
//            1.基于流程信息 id 查询到流程信息的对象
            BpmnInfo bpmnInfo = super.getById(id);
            if (bpmnInfo == null) {
                throw new ServiceException("抱歉，您输入的参数错误，流程信息不存在");
            }
            try {
//                2.基于流程信息中的部署id ，调用 activiti 的api来删除部署信息
                repositoryService.deleteDeployment(bpmnInfo.getDeploymentId());
            } catch (Exception e) {
                e.printStackTrace();
//                3.若删除失败，提示前端有正在进行中的流程，无法删除
                throw new ServiceException("删除失败，当前存在正在进行的审核流程");
            }

        }
//        4.进行批量删除bpmninfo 的数量
        return super.removeBatchByIds(Arrays.asList(ids));
    }


    //页面部署文件 流程图的显示
    @Override
    public InputStream getResourceByType(Long id, String type) {
//       1.基于流程信息id 查询流程信息对象
        BpmnInfo bpmnInfo = this.getById(id);

//       2.基于流程部署id 查询流程定义对象
        ProcessDefinition definition = repositoryService.createProcessDefinitionQuery()
                .deploymentId(bpmnInfo.getDeploymentId())
                .singleResult();//返回单个对象
//       3.判断当前查询的数据类型
//       4.若当前类型是 image ，就查询流程图
        if ("image".equals(type)) {
            // 4.1 通过流程定义对象的图片资源名称, 判断是否有图片资源
            if (StringUtils.isNotEmpty(definition.getDiagramResourceName())) {
                // 4.2 如果有图片, 直接通过仓库 Service 接口获取图片流对象并返回
                return repositoryService.getResourceAsStream(bpmnInfo.getDeploymentId(), definition.getDiagramResourceName());
            } else {
                //4.3 获取 bpmn 模型对象.
                BpmnModel bpmnModel = repositoryService.getBpmnModel(definition.getId());
                //4.4  如果没有图片, 通过流程图生成工具, 生成流程图, 得到流对象并返回
                return processDiagramGenerator.generateDiagram(
                        bpmnModel,
                        "宋体",
                        "宋体",
                        "宋体"
                );
            }
        } else if ("bpmn".equals(type)) {
            // 5. 如果类型是 bpmn, 直接通过仓库 Service 获取文件流对象并返回
            return repositoryService.getResourceAsStream(bpmnInfo.getDeploymentId(), definition.getResourceName());
        }
//        6.若是其他类型 则手动抛异常
        throw new ServiceException("当前查看的文件类型不支持");
    }

}
